import { BaseGraph, IOrigin, IBaseProps } from './graph/base'

export class Transform {
  private graph: BaseGraph
  private children: Array<any> = []
  constructor(graph: BaseGraph) {
    this.graph = graph
    this.children.push(this.graph)
    return this
  }
  public array(count: number, x?: number, y?: number) {
    this.children = new Array(count).fill(undefined).map(() => this.clone(this.children))
    for (let i = 0; i < this.children.length; i++) {
      this.calendar(this.children[i], (graph) => {
        graph.x = graph.x + i * (x || 0)
        graph.y = graph.y + i * (y || 0)
      })
    }
    return this
  }
  public skewX(value: number) {
    this.calendar(this.children, (graph) => (graph.skewX = value))
    return this
  }
  public skewY(value: number) {
    this.calendar(this.children, (graph) => (graph.skewY = value))
    return this
  }
  public skew(skewX: number, skewY: number) {
    this.calendar(this.children, (graph) => {
      graph.skewX = skewX
      graph.skewY = skewY
    })
    return this
  }
  public rotate(angle: number, increment?: boolean, increment2?: boolean) {
    this.calendar(this.children, (graph, index, level) => {
      graph.rotate = angle * (!!increment ? index : 1) * (!!increment2 ? level : 1)
    })
    return this
  }
  public scaleX(value: number, increment?: boolean, increment2?: boolean) {
    this.calendar(
      this.children,
      (graph, index, level) => (graph.scaleX = Math.pow(value, (!!increment ? index : 1) * (!!increment2 ? level : 1)))
    )
    return this
  }
  public scaleY(value: number, increment?: boolean, increment2?: boolean) {
    this.calendar(
      this.children,
      (graph, index, level) => (graph.scaleY = Math.pow(value, (!!increment ? index : 1) * (!!increment2 ? level : 1)))
    )
    return this
  }
  public scale(scaleX: number, scaleY: number, increment?: boolean, increment2?: boolean) {
    this.calendar(this.children, (graph, index, level) => {
      let s = (!!increment ? index : 1) * (!!increment2 ? level : 1)
      graph.scale(Math.pow(scaleX, s), Math.pow(scaleY, s))
    })
    return this
  }
  public alpha(value: number, increment?: boolean) {
    this.calendar(this.children, (graph, index) => {
      graph.option.alpha = 1 - (!!increment ? index : 0) * value
    })
    return this
  }
  public origin(value: IOrigin) {
    this.calendar(this.children, (graph) => (graph.origin = value))
    return this
  }
  public draw(ctx: CanvasRenderingContext2D) {
    this.calendar(this.children, (graph) => graph.draw(ctx))
  }
  public setOption(props?: IBaseProps) {
    this.calendar(this.children, (graph) => graph.setOption(props))
  }
  public mirror(direction: 'x' | 'y' | 'xy', x: number, y: number) {
    this.array(2, x * 2 || 0, y * 2 || 0)
    this.calendar(this.children[1], (graph) => {
      if (direction.match('x')) graph.scaleX = -graph.scaleX
      if (direction.match('y')) graph.scaleY = -graph.scaleY
    })
    return this
  }
  private calendar(
    children: Array<any>,
    callback: (graph: BaseGraph, index: number, level: number) => void,
    index?: number,
    level?: number
  ) {
    level = (level || 0) + 1
    index = index || 0
    for (let i = 0; i < children.length; i++) {
      if (level == 1) index = i
      if (Array.isArray(children[i])) this.calendar(children[i], callback, index, level)
      else callback(children[i], index, level)
    }
  }
  private clone(children: Array<any>) {
    let result = []
    for (let i = 0; i < children.length; i++) {
      if (Array.isArray(children[i])) result.push(this.clone(children[i]))
      else result.push(children[i].clone())
    }
    return result
  }
}
